package org.zoomdev.zoom.common.caster;

import org.zoomdev.zoom.aop.factory.ConstructorFactory;
import org.zoomdev.zoom.common.json.JSON;
import org.zoomdev.zoom.common.utils.BeanUtils;

import java.io.Reader;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

class Maps {

    static class Map2MapProvider extends CasterProviderWithClass {
        @Override
        public ValueCaster getCaster(Class<?> srcType, Class<?> toType) {
            if (Map.class.isAssignableFrom(srcType) && !toType.isInterface()) {
                if (Map.class.isAssignableFrom(toType)) {
                    //也是一个map
                    return new Map2Map(toType);
                }
            }
            return null;
        }
    }

    static class Map2BeanProvider extends CasterProviderWithClass {
        @Override
        public ValueCaster getCaster(Class<?> srcType, Class<?> toType) {
            if (Map.class.isAssignableFrom(srcType) && !toType.isInterface()) {
                if (!Utils.isSimple(toType)) {
                    return new FastMap2BeanCaster(toType);
                }
            }
            return null;
        }
    }

    static class Bean2MapProvider extends CasterProviderWithClass {
        @Override
        public ValueCaster getCaster(Class<?> srcType, Class<?> toType) {
            if (Map.class.isAssignableFrom(toType)) {
                if (!Utils.isSimple(srcType)) {
                    if (toType == Map.class || toType == LinkedHashMap.class) {
                        return new Bean2Map(null);
                    }
                    return new Bean2Map(toType);
                }
            }
            return null;
        }
    }

    static class MapValueCaster implements ValueCaster {

        private final Type[] types;

        public MapValueCaster(ParameterizedType targetType) {
            types = targetType.getActualTypeArguments();
        }

        @Override
        public Object to(Object src) {
            Map map = (Map) src;
            Map dest = new LinkedHashMap();
            Iterator<Map.Entry> iterator = map.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = iterator.next();
                dest.put(Caster.toType(entry.getKey(), types[0]),
                        Caster.toType(entry.getValue(), types[1]));
            }

            return dest;
        }
    }

    static class Reader2Map implements ValueCaster {

        @Override
        public Object to(Object src) {
            try (Reader is = (Reader) src) {
                return JSON.parse(is, Map.class);
            } catch (Exception e) {
                throw new CasterException(e);
            }
        }

    }


    static class Map2Map implements ValueCaster {
        private final Class<?> toType;

        public Map2Map(Class<?> toType) {
            this.toType = toType;
        }

        @SuppressWarnings({"unchecked", "rawtypes"})
        @Override
        public Object to(Object src) {
            Map data = (Map) src;
            try {
                Map result = (Map) toType.newInstance();
                result.putAll(data);
                return result;
            } catch (Exception e) {
                throw new CasterException(e);
            }
        }

    }

    static class Bean2Map implements ValueCaster {
        private final org.zoomdev.zoom.aop.Constructor toType;

        public Bean2Map(Class toType) {
            if (toType == null) {
                this.toType = null;
            } else {
                this.toType = ConstructorFactory.FACTORY.create(toType);
            }
        }

        @SuppressWarnings({"unchecked", "rawtypes"})
        @Override
        public Object to(Object src) {

            try {
                if (toType == null) {
                    return BeanUtils.toMap(src);
                } else {
                    Map dest = (Map) toType.newInstance();
                    return BeanUtils.toMap(src, dest);
                }

            } catch (Exception e) {
                throw new CasterException(e);
            }

        }

    }


}
